<?php 
/**
 * @package scottlib.criteria
 * @author Aaron Collegeman aaroncollegeman.com
 * @version 1.0.0-BETA
 * 
 * Copyright (C)2009 Collegeman.net, LLC.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 */

///////////////////////////////////////////////////////////////////////////////
// This file loads the criteria builder interface and implementations.
///////////////////////////////////////////////////////////////////////////////

abstract class slCriteriaBuilder {
	
	protected $placeholders = array();
	
	protected $criteria;
	
	protected $isPartial = false;
	
	final function build() {
		$fx = "build".strcap(substr(get_class($this->criteria), 2));
		return trim(call_user_func(array($this, $fx)));
	}
	
	protected final function isPartial() {
		return $this->isPartial;
	}
	
	protected final function setIsPartial($isPartial) {
		$this->isPartial = $isPartial; 
	}
	
	final function __call($name, $args) {
		throw new Exception(get_class($this)." does not know how to build ".get_class($this->criteria)." [$name]");
	}
	
	protected final function setPlaceholder($name, $value) {
		$this->placeholders[$name] = $value;
	}
	
	protected final function addPlaceholders($placeholders) {
		foreach($placeholders as $name => $value) {
			$this->setPlaceholder($name, $value);
		}
	}
	
	protected final function getPlaceholders() {
		return $this->placeholders;
	}
	
}

final class slSimpleDBCriteriaBuilder extends slCriteriaBuilder {
	
	private $domain;
	
	function __construct($domain, slCriteria $criteria, $placeholders = array()) {
		$this->domain = $domain;
		$this->criteria = $criteria;
		$this->addPlaceholders($placeholders);
		$this->addPlaceholders($criteria->getPlaceholders());
	}
	
	private function buildSelectAndFrom() {
		return (!$this->isPartial() ? "SELECT itemName() FROM $this->domain WHERE" : '');
	}
	
	private function parseValue($value) {
		foreach($this->placeholders as $n => $v) {
			if (strpos($value, ":$n") !== false) {
				$value = preg_replace("/:$n/", $v, $value);
			}
		}
		
		return "'".preg_replace("/'/", "''", $value)."'";
	}
	
	protected function buildCriteria() {
		return $this->buildAndCriteria();
	}
	
	protected function buildAndCriteria() {
		$exprs = array();
		foreach($this->criteria->getCriterion() as $c) {
			$builder = new slSimpleDBCriteriaBuilder($this->domain, $c, $this->placeholders);
			$builder->setIsPartial(true);
			$exprs[] = $builder->build();
		}
		return $this->buildSelectAndFrom() . ' ' . join(' AND ', $exprs);
	}
	
	protected function buildOrCriteria() {
		$exprs = array();
		foreach($this->criteria->getCriterion() as $c) {
			$builder = new slSimpleDBCriteriaBuilder($this->domain, $this->criteria, $this->placeholders);
			$builder->setIsPartial(true);
			$exprs[] = $builder->build();
		}
		return $this->buildSelectAndFrom() . ' ' . join(' OR ', $exprs);
	}
	
	protected function buildEqCriteria() {
		return $this->buildSelectAndFrom() . sprintf(" ( %s = %s ) ", $this->criteria->getPropertyName(), $this->parseValue($this->criteria->getValue()));
	}
	
	protected function buildNeCriteria() {
		return $this->buildSelectAndFrom() . sprintf(" ( %s != %s ) ", $this->criteria->getPropertyName(), $this->parseValue($this->criteria->getValue()));
	}
	
	protected function buildEachCriteria() {
		$exprs = array();
		foreach($this->criteria->getValues() as $v) {
			$this->setPlaceholder($this->criteria->getPlaceholder(), $v);
			$builder = new slSimpleDBCriteriaBuilder($this->domain, $this->criteria->getCriteria(), $this->placeholders);
			$builder->setIsPartial(true);
			$exprs[] = $builder->build();
		}
		return $this->buildSelectAndFrom() . ' ' . join(' AND ', $exprs);
	}
	
}